package gateway

import (
	"cloud-export-iotkit-mqtt/pkg/config"
	"cloud-export-iotkit-mqtt/pkg/driver"
	"cloud-export-iotkit-mqtt/pkg/iita"
	"cloud-export-iotkit-mqtt/pkg/msg"
	"context"
	"encoding/json"
	"fmt"
	"github.com/edgexfoundry/app-functions-sdk-go/v2/pkg/interfaces"
	"github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger"
	"github.com/google/uuid"
	"time"
)

type GatewayImpl struct {

	// 网关 连接信息
	ChannelConfig config.MQTTConfig
	// 设备属性
	Gateway

	// 已经注册的子设备
	RegisterDeviceMap map[string]iita.Device

	// 已经订阅的子设备
	SubscribeDeviceMap map[string]iita.Device

	lc logger.LoggingClient

	Service interfaces.ApplicationService

	// app通道
	ServiceCh driver.ProtocolServiceChanel

	retryTime      time.Duration
	reconnecting   chan struct{}
	RecvMsgChannel chan interface{}
}

func (g *GatewayImpl) RegisterSelf() error {
	g.SubTopicList()

	return nil
}

func (g *GatewayImpl) RegisterDevice(device iita.Device) error {
	subDeviceRegistyTopic := fmt.Sprintf("/sys/%s/%s/s/register", g.ProductKey, g.DeviceName)

	uuid := uuid.New()
	registryMsg := msg.IotMsgSubDeviceRegistry{uuid.String(), msg.IotMsgSubDeviceRegistryBody{device.ProductKey, device.DeviceName, device.Model}}
	marshal, err2 := json.Marshal(registryMsg)
	if err2 != nil {
		return err2
	}
	err := g.ServiceCh.Publish(subDeviceRegistyTopic, byte(2), marshal, 1)
	g.lc.Infof("注册子设备: %v, err: %v", device, err.Error())

	return nil
}

func (g *GatewayImpl) DeRegisterDeviceByName(deviceName string) error {
	//TODO implement me
	panic("implement me")
}

func (g *GatewayImpl) Run(args ...interface{}) {
	g.ServiceCh.Connect()

}

func (g *GatewayImpl) Stop() error {
	//TODO implement me
	panic("implement me")
}

func (g *GatewayImpl) SubTopicList() {
	// 属性获取

	if g.subscribeTopicByFormat("/sys/%s/%s/c/service/property/get", g.onPropertyGetTopic) {
		return
	}

	// 属性设置
	if g.subscribeTopicByFormat("/sys/%s/%s/c/service/property/set", g.onPropertySetTopic) {
		return
	}

	//订阅服务
	//sys/{productKey}/{deviceName}/c/service/服务名

	//订阅
	//sys/{productKey}/{deviceName}/c/service/property/set

}

func (g *GatewayImpl) subscribeTopicByFormat(strFmt string, callback driver.MessageCallback) bool {
	topic := fmt.Sprintf(strFmt, g.ProductKey, g.DeviceName)
	g.lc.Infof("订阅topic: %v", topic)
	g.ServiceCh.Subscribe(topic, callback)
	// TODO
	return true
}

func (g *GatewayImpl) subscribeTopicByFormat2(strFmt string, productKey string, deviceName string, callback driver.MessageCallback) bool {
	topic := fmt.Sprintf(strFmt, productKey, deviceName)
	g.lc.Infof("订阅topic: %v", topic)
	g.ServiceCh.Subscribe(topic, callback)
	// TODO
	return true
}

func (g *GatewayImpl) Initialize(otherConfig ...interface{}) error {

	// 设置回调
	lc := g.Service.LoggingClient()
	g.lc = lc
	g.ServiceCh.Initialize(&lc, g.ChannelConfig, g.onConnect, g.onConnectLost)
	g.Gateway.Initialize()

	g.retryTime = 1

	return nil
}

func (g *GatewayImpl) onConnect(ch driver.ProtocolServiceChanel) {
	g.RegisterSelf()

	// todo: 优化

	deviceMap := g.refreshSubDeviceMap()

	for _, val := range deviceMap {
		g.AddSubDevice(val)
		g.RegisterDevice(val)
	}

	g.subscribeSubDeviceTopics()
	g.retryTime = 1
}

func (g *GatewayImpl) onConnectLost(ch driver.ProtocolServiceChanel, err error) {
	// 当从连接到断开时才进入这个回调
	g.lc.Warnf("onConnectLost: %v", err)

	select {
	case g.reconnecting <- struct{}{}:
		g.lc.Infof("Connection lost, retrying in %d seconds...", g.retryTime)

		time.AfterFunc(g.retryTime*time.Second, func() {
			// 重连
			g.ServiceCh.Connect()
			<-g.reconnecting
		})
		g.retryTime *= 2
	default:
		g.lc.Infof("Reconnection already in progress")
	}

}

func (g *GatewayImpl) refreshSubDeviceMap() map[string]iita.Device {
	// 获取子设备列表
	background := context.Background()

	deviceClient := g.Service.DeviceClient()

	res, err := deviceClient.AllDevices(background, nil, 0, -1)

	if err != nil {
		g.lc.Errorf("AllDevices returned error: %s", err.Error())
	}
	g.lc.Infof(" device: %v", res.Devices)
	allDeviceMap := map[string]iita.Device{}

	for i, device := range res.Devices {
		productKey := device.ProfileName
		name := device.Name
		g.lc.Infof("%d, deviceName: %s, productKey: %s", i, name, productKey)

		iotKitDevice := iita.Device{ProductKey: productKey, DeviceName: name, DeviceId: name}

		allDeviceMap[name] = iotKitDevice

	}
	return allDeviceMap
}
func (g *GatewayImpl) registrySubDeviceList() {
	//注册子设备

}

// 订阅子设备主题
func (g *GatewayImpl) subscribeSubDeviceTopics() {

	deviceMap := g.GetDeviceMap()
	// 订阅相关主题
	for _, subDevice := range deviceMap {

		// 属性获取
		res := g.subscribeTopicByFormat2("/sys/%s/%s/c/service/property/get", subDevice.ProductKey, subDevice.DeviceName, g.onSubDevicePropertyGetTopic)
		if !res {
			continue
		}
		// 属性设置
		if !g.subscribeTopicByFormat2("/sys/%s/%s/c/service/property/set", subDevice.ProductKey, subDevice.DeviceName, g.onSubDevicePropertySetTopic) {
			continue
		}

		// 服务调用
		if !g.subscribeTopicByFormat2("/sys/%s/%s/c/service/%", subDevice.ProductKey, subDevice.DeviceName, g.onSubDevicePropertyServiceTopic) {
			continue
		}

		// 获取配置
		if !g.subscribeTopicByFormat2("/sys/%s/%s/c/config/set", subDevice.ProductKey, subDevice.DeviceName, g.onSubDeviceConfigSetTopic) {
			continue
		}

	}
}

// 子设备

func (g *GatewayImpl) onSubDevicePropertyGetTopic(message msg.Message) {
	g.lc.Infof("onSubDevicePropertyGetTopic: %s", message.Payload())
	cmdClient := g.Service.CommandClient()
	if cmdClient == nil {
		g.lc.Warn("命令服务未配置")
		return
	}

	var msg msg.IotMsgGetProperty

	json.Unmarshal(message.Payload(), &msg)

	topic := message.Topic()
	deviceId := iita.GetDeviceIdByTopic(topic)

	params := msg.Params
	if i := len(params); i > 1 || i == 0 {
		g.lc.Warn("只支持单个值的获取")
		return
	}
	settings := map[string]string{}
	var command string

	g.lc.Infof("%s 设置属性 %s", deviceId, settings)

	response, err := cmdClient.IssueGetCommandByNameWithQueryParams(context.Background(), deviceId, command, settings)
	g.lc.Info("response: %v, err: %s", response, err)

}

func (g *GatewayImpl) onSubDevicePropertySetTopic(message msg.Message) {
	g.lc.Infof("onSubDevicePropertySetTopic: %s", message.Payload())
	cmdClient := g.Service.CommandClient()
	if cmdClient == nil {
		g.lc.Warn("命令服务未配置")
		return
	}
	var msg msg.IotMsgSetProperty

	json.Unmarshal(message.Payload(), &msg)
	topic := message.Topic()
	deviceId := iita.GetDeviceIdByTopic(topic)

	params := msg.Params
	if i := len(params); i > 1 || i == 0 {
		g.lc.Warn("只支持单个值的设置")
		return
	}

	settings := make(map[string]interface{})
	var command string
	for k, v := range params {
		settings[k] = v
		command = k
	}

	g.lc.Infof("%s 设置属性 %s", deviceId, settings)

	response, err := cmdClient.IssueSetCommandByNameWithObject(context.Background(), deviceId, command, settings)
	g.lc.Info("response: %v, err: %s", response, err)

}

func (g *GatewayImpl) onPropertyGetTopic(message msg.Message) {

	var data map[string]interface{}
	json.Unmarshal(message.Payload(), &data)
}

func (g *GatewayImpl) onPropertySetTopic(message msg.Message) {
	g.lc.Infof("onPropertySetTopic")

	var data map[string]interface{}
	json.Unmarshal(message.Payload(), &data)
}

func (g *GatewayImpl) onSubDevicePropertyServiceTopic(message msg.Message) {
	g.lc.Infof("服务调用 onSubDevicePropertyServiceTopic: %s", message.Payload())
	cmdClient := g.Service.CommandClient()
	if cmdClient == nil {
		g.lc.Warn("命令服务未配置")
		return
	}
	var msg msg.IotMsgServiceInvoke

	json.Unmarshal(message.Payload(), &msg)
	topic := message.Topic()
	deviceId := iita.GetDeviceIdByTopic(topic)

	params := msg.Params

	serviceName := iita.GetServiceNameByTopic(topic)
	response, err := cmdClient.IssueSetCommandByNameWithObject(context.Background(), deviceId, serviceName, params)
	g.lc.Info("response: %v, err: %s", response, err)

}

func (g *GatewayImpl) onSubDeviceConfigSetTopic(message msg.Message) {
	g.lc.Infof("属性配置 onSubDeviceConfigSetTopic: %s", message.Payload())
	cmdClient := g.Service.CommandClient()
	if cmdClient == nil {
		g.lc.Warn("命令服务未配置")
		return
	}
}
